// Time: 2022-04-18 14:07:49
// Desc: C用例基础公共函数库

#include "common.h"
#include <ctype.h>
#include <stdio.h>
#include <errno.h>
#include <stdarg.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <limits.h>
#include <string.h>
#include <libgen.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <regex.h>
#include <time.h>
#include <unistd.h>

struct tst_tc_control {
    // 用例名
    char tc_name[PATH_MAX];
    // 用例参数
    int tc_argc;
    char **tc_argv;
    // 用例源文件路径
    char tc_source_path[PATH_MAX];
    // 用例可执行文件路径
    char tc_exec_path[PATH_MAX];
    // 执行用例文件时的目录
    char exec_cwd[PATH_MAX];
    // TST_TC_CWD 测试用例CWD
    char tst_tc_cwd[PATH_MAX];
    // 用例开始执行的时间
    struct tst_time time_start;
    // TST_TC_PID 测试用例主进程pid
    pid_t tst_tc_pid;
    // 标记tc_setup是否被调用
    int tc_setup_called;
    // 用例执行状态
    int tc_stat;
    // TST_TS_TOPDIR 测试套顶层目录
    char tst_ts_topdir[PATH_MAX];
    // TST_COMMON_TOPDIR common顶层目录
    char tst_common_topdir[PATH_MAX];
    // TST_TS_SYSDIR 测试套公共运行目录
    char tst_ts_sysdir[PATH_MAX];
    // TST_TC_SYSDIR 测试用例管理用临时目录
    char tst_tc_sysdir[PATH_MAX];
    // 测试套执行状态标记文件
    char tst_ts_setup_stat[PATH_MAX];
    // 测试结果文件
    char tst_ts_result_file[PATH_MAX];
    char tst_tc_path[PATH_MAX];
};

struct tst_tc_control *g_tst_tc_control;

void show_tc_control(void) {
    dbg("tc_name: %s", g_tst_tc_control->tc_name);
    dbg("tc_source_path: %s", g_tst_tc_control->tc_source_path);
    dbg("tc_exec_path: %s", g_tst_tc_control->tc_exec_path);
    dbg("exec_cwd: %s", g_tst_tc_control->exec_cwd);
    dbg("tst_tc_cwd: %s", g_tst_tc_control->tst_tc_cwd);
    dbg("tst_tc_pid: %d", g_tst_tc_control->tst_tc_pid);
    dbg("tc_stat: %d", g_tst_tc_control->tc_stat);
    dbg("tst_ts_topdir: %s", g_tst_tc_control->tst_ts_topdir);
    dbg("tst_ts_sysdir: %s", g_tst_tc_control->tst_ts_sysdir);
    dbg("tst_tc_sysdir: %s", g_tst_tc_control->tst_tc_sysdir);
    dbg("tst_ts_setup_stat: %s", g_tst_tc_control->tst_ts_setup_stat);
}

int _print(const char *format, ...) {
    va_list ap;
    int ret;
    char buff[MAX_LOG_LEN] = {0};

    va_start(ap, format);
    ret = vsnprintf(buff, MAX_LOG_LEN - 1, format, ap);
    va_end(ap);

    if (buff[ret - 1] != '\n') {
        buff[ret] = '\n';
        buff[ret + 1] = '\0';
    }
    fprintf(stderr, "%s", buff);

    return ret;
}

static int _is_main_process(void) {
    if (g_tst_tc_control->tst_tc_pid == getpid()) {
        return 1;
    }
    return 0;
}

static void _set_tcstat(int stat) {
    g_tst_tc_control->tc_stat = stat;
}

static int _get_tcstat(void) {
    return g_tst_tc_control->tc_stat;
}

void _tc_pass(void) {
    // 只有初始状态的用例才能置为PASS，其他异常状态的用例不能从异常变为PASS
    if (_get_tcstat() == TST_INIT) {
        _set_tcstat(TST_PASS);
    }
}

void _tc_fail(void) {
    if (_get_tcstat() != TST_FAIL) {
        msg("the testcase first fail here");
    }
    _set_tcstat(TST_FAIL);
}

static char *_tcstat_to_str(int tcstat) {
    switch (tcstat) {
        case TST_PASS:
            return "TST_PASS";
        case TST_FAIL:
            return "TST_FAIL";
        case TST_INIT:
            return "TST_INIT";
        case TST_SKIP:
            return "TST_SKIP";
        default:
            return "TST_UNKNOWN";
    }
}

static int _tc_teardown(int argc, char **argv) {
    int ret = 0;

    // 只有用例的主进程才能执行此函数
    if (!_is_main_process()) {
        return 0;
    }
    if (tc_teardown) {
        if (tc_teardown(argc, argv) == 0) {
            msg("call tc_teardown success");
        } else {
            err("call tc_teardown fail");
            ret = 1;
        }
    } else {
        msg("tc_teardown not define");
    }

    return ret;
}

static int _tc_teardown_common(int argc, char **argv) {
    int ret = 0;

    // 只有用例的主进程才能执行此函数
    if (!_is_main_process()) {
        return 0;
    }
    if (tc_teardown_common) {
        if (tc_teardown_common(argc, argv) == 0) {
            msg("call tc_teardown_common success");
        } else {
            err("call tc_teardown_common fail");
            ret = 1;
        }
    } else {
        msg("tc_teardown_common not define");
    }

    return ret;
}

static int _upload_debug_file(void) {
    char upload_dir[PATH_MAX];
    char *flag = getenv("TST_UPLOAD_FAIL_LOG");
    if ((flag != NULL) && ((strcmp(flag, "1") == 0) || (strcmp(flag, "yes") == 0) || (strcmp(flag, "true") == 0))) {
        msg("upload test fail log to service");
    } else {
        return 0;
    }
    snprintf(upload_dir, PATH_MAX, "%s/upload", g_tst_tc_control->tst_tc_sysdir);
    if (!is_exist(upload_dir)) {
        if (mkdirs(upload_dir, 0755) != 0) {
            msg("mkdirs %s fail", upload_dir);
            return -1;
        }
    }
    // python3 "$TST_COMMON_TOPDIR/cmd/upload-logs.py" "${TST_TC_SYSDIR}/upload" "$TST_TC_NAME"
    return command("python3 %s/cmd/upload-logs.py %s %s", g_tst_tc_control->tst_common_topdir, upload_dir,
                   g_tst_tc_control->tc_name);
}

// 编辑测试结果文件
static void append_to_file(const char *filename, const char *content) {
    // 以追加模式打开文件
    FILE *f = fopen(filename, "a");
    if (f == NULL) {
        fprintf(stderr, "Error opening file: %s\n", filename);
        exit(1);
    }

    // 将字符串写入文件
    fputs(content, f);

    // 关闭文件
    fclose(f);
}

// 定义 get_timestamp_ms 函数
static long long get_timestamp_ms() {
    struct timespec ts;
    clock_gettime(CLOCK_REALTIME, &ts);
    return (long long)ts.tv_sec * 1000 + (long long)ts.tv_nsec / 1000000;
}

char* get_tc_attr(const char* attr) {
    FILE* f = fopen(g_tst_tc_control->tst_tc_path, "r");
    if (f == NULL) {
        printf("Error opening file %s\n", g_tst_tc_control->tst_tc_path);
        return NULL;
    }

    char* result = NULL;
    char line[1024];
    while (fgets(line, sizeof(line), f)) {
        // 处理 ^M 字符
        char* cr = strchr(line, '\r');
        if (cr != NULL) {
            *cr = '\0';
        }
        char pattern[256];
        sprintf(pattern, "@%s:", attr);
        if (strstr(line, pattern) == NULL) {
            continue;
        }

        regex_t regex;
        if (regcomp(&regex, pattern, REG_EXTENDED) != 0) {
            printf("Error compiling regex\n");
            break;
        }

        regmatch_t match[1];
        if (regexec(&regex, line, 1, match, 0) == 0) {
            int start = match[0].rm_eo;
            int end = strlen(line) - 1;
            if (line[end] == '\n') {
                end--;
            }
             // 寻找第一个非空格字符的位置
            while (start <= end && isspace(line[start])) {
                start++;
            }
            result = (char*)malloc(end - start + 2);
            strncpy(result, line + start, end - start + 1);
            result[end - start + 1] = '\0';
        }

        regfree(&regex);
        break;
    }

    fclose(f);
    return result;
}

// 从一个文件中读取一个特定的键值对
void read_config_value(const char* file_path, const char* key, char* value, size_t value_size) {
    FILE* file = fopen(file_path, "r");
    if (!file) {
        return;
    }

    char line[256];
    while (fgets(line, sizeof(line), file)) {
        if (strstr(line, key)) {
            strncpy(value, strchr(line, '=') + 1, value_size - 1);
            value[strcspn(value, "\n")] = 0; // Remove newline character
            break;
        }
    }

    fclose(file);
}

// allure相关
typedef struct {
    char start_time[100];
    char end_time[100];
    char allure_case_result[100];
} ResultInfo;

// 获取result文件中的指标项
ResultInfo get_json_from_result_file() {
    ResultInfo result = {0}; // 初始化result结构体，将所有字段置零
    FILE *file = fopen(g_tst_tc_control->tst_ts_result_file, "r");
    if (file == NULL) {
        perror("Error opening file");
        return result;
    }

    char lines[4][100];
    int line_count = 0;
    while (fgets(lines[line_count % 4], 100, file) != NULL) {
        line_count++;
    }
    fclose(file);

    regex_t regex;
    regmatch_t matches[2];
    char pattern[100];
    int i;
    for (i = 0; i < 4; i++) {
        if (strstr(lines[i], "case-start-time:") != NULL) {
            strcpy(pattern, "case-start-time: ([0-9]+)");
            regcomp(&regex, pattern, REG_EXTENDED);
            if (regexec(&regex, lines[i], 2, matches, 0) == 0) {
                strncpy(result.start_time, lines[i] + matches[1].rm_so, matches[1].rm_eo - matches[1].rm_so);
                // 处理特殊字符
                for (int j = 0; result.start_time[j]; j++) {
                    if (!isdigit(result.start_time[j])) {
                        result.start_time[j] = '\0';
                        break;
                    }
                }
            }
            regfree(&regex);
        } else if (strstr(lines[i], "case-end-time:") != NULL) {
            strcpy(pattern, "case-end-time: ([0-9]+)");
            regcomp(&regex, pattern, REG_EXTENDED);
            if (regexec(&regex, lines[i], 2, matches, 0) == 0) {
                strncpy(result.end_time, lines[i] + matches[1].rm_so, matches[1].rm_eo - matches[1].rm_so);
            }
            regfree(&regex);
        } else if (strstr(lines[i], "case-result:") != NULL) {
            strcpy(pattern, "case-result: (\\w+)");
            regcomp(&regex, pattern, REG_EXTENDED);
            if (regexec(&regex, lines[i], 2, matches, 0) == 0) {
                strncpy(result.allure_case_result, lines[i] + matches[1].rm_so, matches[1].rm_eo - matches[1].rm_so);
            }
            regfree(&regex);
        }
    }

    // 处理测试结果
    if (strcasecmp(result.allure_case_result, "PASS") == 0) {
        strcpy(result.allure_case_result, "passed");
    } else if (strcasecmp(result.allure_case_result, "FAIL") == 0) {
        strcpy(result.allure_case_result, "failed");
    } else if (strcasecmp(result.allure_case_result, "SKIP") == 0) {
        strcpy(result.allure_case_result, "skipped");
    } else {
        strcpy(result.allure_case_result, "broken");
    }

    return result;
}

char* _get_system_info(const char *info_type) {
    char info[256];
    char *result = NULL;
    if (strcmp(info_type, "kernel_version") == 0) {
        FILE *fp = popen("uname -r", "r");
        if (fp) {
            fgets(info, sizeof(info), fp);
            pclose(fp);
        }
    } else if (strcmp(info_type, "os_version") == 0) {
        FILE *fp = fopen("/etc/system-release", "r");
        if (fp) {
            fgets(info, sizeof(info), fp);
            fclose(fp);
        }
    } else if (strcmp(info_type, "arch") == 0) {
        FILE *fp = popen("arch", "r");
        if (fp) {
            fgets(info, sizeof(info), fp);
            pclose(fp);
        }
    }
    size_t len = strlen(info);
    if (len > 0 && info[len - 1] == '\n') {
        info[len - 1] = '\0';
    }
    result = strdup(info);
    return result;
}

// 生成allure-result-json文件
void make_allure_json() {
    char tst_allure_json_file[1024];
    ResultInfo result = get_json_from_result_file();
    if (strcmp(result.allure_case_result, "passed") != 0 && strcmp(result.allure_case_result, "skipped") != 0) {
        char *pos = strstr(g_tst_tc_control->tc_name, ".test");
        if (pos != NULL && pos >= g_tst_tc_control->tc_name) {
            // 获取替换前和替换后的字符串长度
            size_t len_before = strlen(".test");
            // 使用 snprintf 计算替换后的总长度
            size_t total_len = snprintf(NULL, 0, "%.*s%s%s", (int)(pos - g_tst_tc_control->tc_name), g_tst_tc_control->tc_name, ".c", pos + len_before) + 1;

            if (total_len <= sizeof(tst_allure_json_file)) {
                strcpy(pos, ".c");
            } else {
                printf("Allure_json_Error: Buffer overflow detected in tst_allure_json_file.\n");
            }
        }
        snprintf(tst_allure_json_file, sizeof(tst_allure_json_file), "%s/logs/allure_data/%s-result.json", g_tst_tc_control->tst_ts_topdir, g_tst_tc_control->tc_name);

        FILE *file = fopen(tst_allure_json_file, "w");
        if (file == NULL) {
            perror("Error opening file");
            return;
        }
        char *kernel_version = _get_system_info("kernel_version");
        char *os_version = _get_system_info("os_version");
        char *arch = _get_system_info("arch");
        char image_url[256] = "xxx";
        char node_id[256] = "xxx";
        char temp_dirname[256];
        strcpy(temp_dirname, g_tst_tc_control->tst_ts_topdir);
        char *pro_name = strrchr(dirname(temp_dirname), '/') + 1;
        if (pro_name == NULL) {
            pro_name = "xxx"; // 将 pro_name 设置为默认值 "xxx"
        }
        fprintf(file, "{\n");
        fprintf(file, "  \"uuid\": \"%s\",\n", get_tc_attr("用例ID"));
        fprintf(file, "  \"name\": \"%s\",\n", g_tst_tc_control->tc_name);
        fprintf(file, "  \"description\": \"%s\",\n", get_tc_attr("用例描述"));
        fprintf(file, "  \"status\": \"%s\",\n", result.allure_case_result);
        fprintf(file, "  \"start\": \"%s\",\n", result.start_time);
        fprintf(file, "  \"stop\": \"%s\",\n", result.end_time);
        fprintf(file, "  \"labels\": [{\n");
        fprintf(file, "    \"name\": \"story\",\n");
        fprintf(file, "    \"value\": \"%s\"\n", strrchr(g_tst_tc_control->tst_ts_topdir, '/') + 1);
        fprintf(file, "  }],\n");
        fprintf(file, "  \"parameters\": [{\n");
        fprintf(file, "    \"name\": \"os信息\",\n");
        fprintf(file, "    \"value\": \"%s\"\n", os_version);
        fprintf(file, "  }, {\n");
        fprintf(file, "    \"name\": \"内核版本\",\n");
        fprintf(file, "    \"value\": \"%s\"\n", kernel_version);
        fprintf(file, "  }, {\n");
        fprintf(file, "    \"name\": \"架构类型\",\n");
        fprintf(file, "    \"value\": \"%s\"\n", arch);
        if (is_exist("/etc/tst-env.conf")) {
            read_config_value("/etc/tst-env.conf", "image_url=", image_url, sizeof(image_url));
            read_config_value("/etc/tst-env.conf", "node_id=", node_id, sizeof(node_id));
            if (strcmp(image_url, "xxx") != 0) {
                fprintf(file, "  },\n");
                fprintf(file, "  {\n");
                fprintf(file, "    \"name\": \"镜像地址\",\n");
                fprintf(file, "    \"value\": \"%s\"\n", image_url);
            }
            if (strcmp(node_id, "xxx") != 0) {
                fprintf(file, "  },\n");
                fprintf(file, "  {\n");
                fprintf(file, "    \"name\": \"ndeploy节点\",\n");
                fprintf(file, "    \"value\": \"ndeploy ssh vm -n %s\"\n", node_id);
            }
        }
        fprintf(file, "  }, {\n");
        fprintf(file, "    \"name\": \"复现步骤\",\n");
        fprintf(file, " \"value\": \"(1)：ndeploy new vm -i %s (2)：git clone --recurse-submodules git@git.woa.com:tlinux/TST/%s (3)：cd %s/%s (4)：./tsuite compile (5)：./tsuite run testcase/%s\"\n",
            image_url,
            pro_name,
            pro_name,
            strrchr(g_tst_tc_control->tst_ts_topdir, '/') + 1,
            g_tst_tc_control->tc_name);
        fprintf(file, "  }],\n");
        fprintf(file, "  \"attachments\": [{\n");
        fprintf(file, "    \"name\": \"log\",\n");
        fprintf(file, "    \"source\": \"%s.log\",\n", g_tst_tc_control->tc_name);
        fprintf(file, "    \"type\": \"text/plain\"\n");
        fprintf(file, "  }]\n");
        fprintf(file, "}\n");

        fclose(file);
    }

}


static void _tc_run_complete(int argc, char **argv) {
    int ret = 0;

    // 只有用例的主进程才能执行此函数
    if (!_is_main_process()) {
        return;
    }
    // tc_setup有调用时tc_teardown才会被调用
    if (g_tst_tc_control->tc_setup_called) {
        if (_tc_teardown(argc, argv) == 0) {
            msg("call _tc_teardown success");
        } else {
            msg("call _tc_teardown fail");
            ret = 1;
        }
    } else {
        msg("the tc_setup not called, so tc_teardown ignore");
    }
    if (_tc_teardown_common(argc, argv) == 0) {
        msg("call _tc_teardown_common success");
    } else {
        msg("call _tc_teardown_common fail");
        ret = 1;
    }

    // TCase自动化执行框架需要用这个输出判断用例是否支持完
    msg("Global test environment tear-down");
    switch (_get_tcstat()) {
        case TST_PASS:
            msg("RESULT : %s ==> [  PASSED  ]", g_tst_tc_control->tc_name);
            append_to_file(g_tst_tc_control->tst_ts_result_file, "case-result: PASS\n");
            break;
        case TST_FAIL:
            (void) _upload_debug_file();
            msg("RESULT : %s ==> [  FAILED  ]", g_tst_tc_control->tc_name);
            append_to_file(g_tst_tc_control->tst_ts_result_file, "case-result: FAIL\n");
            ret = 1;
            break;
        case TST_INIT:
            (void) _upload_debug_file();
            msg("RESULT : %s ==> [  NOTEST  ]", g_tst_tc_control->tc_name);
            append_to_file(g_tst_tc_control->tst_ts_result_file, "case-result: NOTEST\n");
            ret = 1;
            break;
        case TST_SKIP:
            msg("RESULT : %s ==> [  SKIP  ]", g_tst_tc_control->tc_name);
            append_to_file(g_tst_tc_control->tst_ts_result_file, "case-result: SKIP\n");
            ret = 0;
            break;
        default:
            (void) _upload_debug_file();
            msg("RESULT : %s ==> [  UNKNOWN  ]", g_tst_tc_control->tc_name);
            append_to_file(g_tst_tc_control->tst_ts_result_file, "case-result: UNKNOWN\n");
            ret = 1;
            break;
    }
    msg("cost %.9Lf", (long double) tst_time_since_now(&(g_tst_tc_control->time_start)) / NS_PER_SEC);
    //延时0.1s
    usleep(100000);
    long long case_end_time = get_timestamp_ms();
    char end_time_str[1024];
    snprintf(end_time_str, sizeof(end_time_str), "case-end-time: %lld\n\n", case_end_time);
    append_to_file(g_tst_tc_control->tst_ts_result_file, end_time_str);
    make_allure_json();
    exit(ret);
}

void _tst_assert(const char *expr, const char *file, int line, const char *func) {
    msg("%s:%d %s assert '%s' fail", file, line, func, expr);
    _tc_run_complete(g_tst_tc_control->tc_argc, g_tst_tc_control->tc_argv);
}

void _skip_test(const char *message, const char *file, int line, const char *func) {
    int tc_stat = _get_tcstat();

    if ((tc_stat == TST_PASS) || (tc_stat == TST_INIT) || (tc_stat == TST_SKIP)) {
        _set_tcstat(TST_SKIP);
        msg("%s:%d %s: set testcase SKIP: %s", file, line, func, message);
    } else {
        msg("%s:%d %s: set testcase SKIP fail: %s", file, line, func, message);
        err("the testcase stat is %s, can't set to SKIP", _tcstat_to_str(tc_stat));
    }
    _tc_run_complete(g_tst_tc_control->tc_argc, g_tst_tc_control->tc_argv);
}

static int _tc_setup_common(int argc, char **argv) {
    int ret = 0;

    if (tc_setup_common) {
        if (tc_setup_common(argc, argv) == 0) {
            msg("call tc_setup_common success");
        } else {
            err("call tc_setup_common fail");
            ret = 1;
        }
    } else {
        msg("tc_setup_common not define");
    }

    return ret;
}

static int _tc_setup(int argc, char **argv) {
    int ret = 0;

    // 只有用例的主进程才能执行此函数
    if (!_is_main_process()) {
        return 0;
    }
    g_tst_tc_control->tc_setup_called = 1;
    if (tc_setup) {
        if (tc_setup(argc, argv) == 0) {
            msg("call tc_setup success");
        } else {
            err("call tc_setup fail");
            ret = 1;
        }
    } else {
        msg("tc_setup not define");
    }

    return ret;
}

static int _do_test(int argc, char **argv) {
    int ret = 0;

    // 只有用例的主进程才能执行此函数
    if (!_is_main_process()) {
        return 0;
    }
    if (do_test) {
        if (do_test(argc, argv) == 0) {
            msg("call do_test success");
        } else {
            err("call do_test fail");
            ret = 1;
        }
    } else {
        err("do_test not define");
        ret = 1;
    }

    return ret;
}

static int _is_ts_setup_called(void) {
    if (is_file(g_tst_tc_control->tst_ts_setup_stat)) {
        return 1;
    }
    return 0;
}

static int _get_ts_setup_stat(void) {
    char buff[PAGE_SIZE];
    int read_size = read_line(g_tst_tc_control->tst_ts_setup_stat, 1, buff, PAGE_SIZE);
    if (read_size <= 0) {
        return TST_INIT;
    }
    return (int) strtol(buff, NULL, 0);
}

// 功能：判断路径是否为文件并且存在
// 参数：
//   path -- 文件路径
// 返回值：
//   0 -- 文件不存在
//   1 -- 文件存在
int is_file(const char *path) {
    struct stat sb;

    if (stat(path, &sb) != 0) {
        return 0;
    }
    if (S_ISDIR(sb.st_mode)) {
        return 0;
    }
    return 1;
}

// 功能：判断路径是否为目录并且存在
// 参数：
//   path -- 文件路径
// 返回值：
//   0 -- 文件不存在
//   1 -- 文件存在
int is_dir(const char *path) {
    struct stat sb;

    if (stat(path, &sb) != 0) {
        return 0;
    }
    if (S_ISDIR(sb.st_mode)) {
        return 1;
    }
    return 0;
}

// 功能：判断路径是否存在
// 参数：
//   path -- 文件路径
// 返回值：
//   0 -- 文件不存在
//   1 -- 文件存在
int is_exist(const char *path) {
    if (access(path, F_OK) == 0) {
        return 1;
    } else {
        return 0;
    }
}

// 功能：读文件内容到buff中
// 参数：
//   path -- 文件路径
//   buff -- 接收文件内容
//   size -- buff大小
// 返回值：
//   -1 -- 文件读取失败
//   其他 -- 读取到的文件内容长度
size_t read_file(const char *path, char *buff, size_t size) {
    int fd = open(path, O_RDONLY);
    if (fd < 0) {
        perror("Error opening file");
        return -1;
    }
    ssize_t ret = read(fd, buff, size);
    if (ret < 0) {
        perror("Error reading file");
    }
    close(fd);
    return ret;
}

// 功能：读文本文件指定行的内容到buff中
// 参数：
//   path -- 文件路径
//   line -- 要读取内容的行号（从1开始）
//   buff -- 接收文件内容
//   size -- buff大小
// 返回值：
//   -1 -- 文件读取失败
//   其他 -- 读取到的文件内容长度
size_t read_line(const char *path, int line, char *buff, size_t size) {
    int i = 0;
    FILE *file = NULL;
    if (line <= 0) {
        dbg("the line less then 0: %d", line);
        return -1;
    }
    file = fopen(path, "r");
    if (file == NULL) {
        dbg("open file %s fail, errno %d, error: %s", path, errno, strerror(errno));
        return -1;
    }
    while (i < line) {
        i++;
        if (fgets(buff, size, file) == NULL) {
            if (i == line) {
                // 刚好读到指定行的时候文件结束
                break;
            }
            // 文件读完了，但是行号还没有读到
            dbg("get line %d fail, buff: %s, errno %d, error: %s", i, buff, errno, strerror(errno));
            fclose(file);
            return -1;
        }
    }
    fclose(file);
    return strlen(buff);
}

// 功能：创建多层目录
// 参数：
//   dir -- 目录
//   mode -- 目录权限
// 返回值：
//   -1 -- 创建失败
//    0 -- 创建成功
int mkdirs(const char *dir, mode_t mode) {
    int i;
    char now_path[PATH_MAX];

    for (i = 0; i < min(strlen(dir), PATH_MAX - 1); i++) {
        if (dir[i] != '/') {
            continue;
        }
        strncpy(now_path, dir, i + 1);
        if (is_exist(now_path)) {
            if (is_dir(now_path)) {
                continue;
            } else {
                dbg("the path %s exist but not dir", now_path);
                return -1;
            }
        }
        if (mkdir(now_path, mode) != 0) {
            dbg("mkdir %s fail, errno %d, error: %s", now_path, errno, strerror(errno));
            return -1;
        }
    }
    if (mkdir(dir, mode) != 0) {
        dbg("mkdir %s fail, errno %d, error: %s", dir, errno, strerror(errno));
        return -1;
    }
    if (!is_dir(dir)) {
        dbg("mkdirs %s fail", dir);
        return -1;
    }
    return 0;
}

// 功能：执行命令
// 参数：
//   command_format -- 命令内容
// 返回值：同system函数
int command(const char *command_format, ...) {
    va_list ap;
    char cmd[MAX_CMD_LEN] = {0};

    va_start(ap, command_format);
    (void) vsnprintf(cmd, MAX_CMD_LEN, command_format, ap);
    va_end(ap);

    return system(cmd);
}

// 控制台打印用例文件中的描述
int _show_case_attr(const char *filename) {
    msg("==================== vvv 用例属性 vvv ====================\n");
    size_t file_size = 1024 * 10; // 文件大小不超过10KB
    char *buffer = (char *)malloc(file_size); // 分配足够的内存来存储文件内容

    if (buffer == NULL) {
        msg("Memory allocation failed.\n");
        return -1;
    }

    size_t bytes_read = read_file(filename, buffer, file_size);
    if (bytes_read == (size_t)-1) {
        msg("Error reading file: %s\n", filename);
        free(buffer);
        return -1;
    }
    // 添加空字符以确保字符串正确结束
    buffer[bytes_read] = '\0';

    // 使用 strsep 分割文件内容，逐行处理
    char *line = strsep(&buffer, "\n");
    while (line != NULL) {
        // 删除行尾的回车符
        char *end = strchr(line, '\r');
        if (end != NULL) {
            *end = '\0';
        }
        // 检查行中是否包含@符号
        char *at_symbol = strstr(line, "@");
        if (at_symbol != NULL) {
            // 从@符号后的内容开始打印
            msg("%s\n", at_symbol);
        }
        line = strsep(&buffer, "\n");
    }
    msg("==================== ^^^ 用例属性 ^^^ ====================\n");
    free(buffer);
    return 0;
}

static int _tc_init(int argc, char **argv) {
    size_t control_size = ((sizeof(struct tst_tc_control) / PAGE_SIZE) + 1) * PAGE_SIZE;
    g_tst_tc_control = (struct tst_tc_control *) mmap(NULL, control_size, PROT_READ | PROT_WRITE,
                                                      MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    if (g_tst_tc_control == NULL) {
        msg("mmap g_tst_tc_control fail, errno: %d, %s", errno, strerror(errno));
        return 1;
    }
    memset(g_tst_tc_control, 0, control_size);
    if (tst_get_time(&(g_tst_tc_control->time_start)) != 0) {
        msg("tst_get_time fail, errno: %d, %s", errno, strerror(errno));
        return 1;
    }
    strncpy(g_tst_tc_control->tc_name, basename(argv[0]), PATH_MAX);
    g_tst_tc_control->tc_argc = argc;
    g_tst_tc_control->tc_argv = argv;

    if (realpath(argv[0], g_tst_tc_control->tc_exec_path) == NULL) {
        msg("get the realpath fail, errno: %d, %s", errno, strerror(errno));
        return 1;
    }

    strncpy(g_tst_tc_control->tc_source_path, g_tst_tc_control->tc_exec_path, PATH_MAX);
    strncat(g_tst_tc_control->tc_source_path, ".c", PATH_MAX);

    if (getcwd(g_tst_tc_control->exec_cwd, PATH_MAX) == NULL) {
        msg("get cwd fail, errno: %d, %s", errno, strerror(errno));
        return 1;
    }

    strncpy(g_tst_tc_control->tst_tc_cwd, g_tst_tc_control->tc_exec_path, PATH_MAX);
    dirname(g_tst_tc_control->tst_tc_cwd);

    g_tst_tc_control->tst_tc_pid = getpid();
    g_tst_tc_control->tc_stat = TST_INIT;

    // 尝试获取TST_TS_TOPDIR
    strncpy(g_tst_tc_control->tst_ts_topdir, g_tst_tc_control->tc_exec_path, PATH_MAX);
    char path_tst_common[PATH_MAX];
    char path_cmd[PATH_MAX];
    char path_testcase[PATH_MAX];
    char path_tsuite[PATH_MAX];
    while (g_tst_tc_control->tst_ts_topdir[1] != '\0') {
        snprintf(path_tst_common, PATH_MAX, "%s/tst_common", g_tst_tc_control->tst_ts_topdir);
        snprintf(path_cmd, PATH_MAX, "%s/cmd", g_tst_tc_control->tst_ts_topdir);
        snprintf(path_testcase, PATH_MAX, "%s/testcase", g_tst_tc_control->tst_ts_topdir);
        snprintf(path_tsuite, PATH_MAX, "%s/tsuite", g_tst_tc_control->tst_ts_topdir);
        if (is_dir(path_cmd) && is_dir(path_testcase) && is_file(path_tsuite)) {
            if (is_dir(path_tst_common)) {
                strncpy(g_tst_tc_control->tst_common_topdir, path_tst_common, PATH_MAX);
            } else {
                strncpy(g_tst_tc_control->tst_common_topdir, g_tst_tc_control->tst_ts_topdir, PATH_MAX);
            }
            break;
        }
        if (g_tst_tc_control->tst_ts_topdir[1] == '\0') {
            memset(g_tst_tc_control->tst_ts_topdir, 0, PATH_MAX);
            memset(g_tst_tc_control->tst_common_topdir, 0, PATH_MAX);
            break;
        }
        dirname(g_tst_tc_control->tst_ts_topdir);
    }
    char *ext = strstr(g_tst_tc_control->tc_exec_path, ".test");
    // TST_TS_TOPDIR成功获取后，TST_TS_SYSDIR和TST_TC_SYSDIR可以生成了
    if (g_tst_tc_control->tst_ts_topdir[0] != '\0') {
        snprintf(g_tst_tc_control->tst_ts_sysdir, PATH_MAX, "%s/logs/.ts.sysdir", g_tst_tc_control->tst_ts_topdir);
        snprintf(g_tst_tc_control->tst_tc_sysdir, PATH_MAX, "%s/logs/testcase/.tc.%d.sysdir",
                 g_tst_tc_control->tst_ts_topdir, g_tst_tc_control->tst_tc_pid);
        snprintf(g_tst_tc_control->tst_ts_setup_stat, PATH_MAX, "%s/ts.setup.stat", g_tst_tc_control->tst_ts_sysdir);
        // 生成tst_ts_result_file
        snprintf(g_tst_tc_control->tst_ts_result_file, PATH_MAX, "%s/logs/report.result", g_tst_tc_control->tst_ts_topdir);
        // 生成c用例路径
        snprintf(g_tst_tc_control->tst_tc_path, PATH_MAX, "%.*s.c", (int)(ext - g_tst_tc_control->tc_exec_path), g_tst_tc_control->tc_exec_path);
    }
    // 控制台打印用例属性，allure用例log文件需要展示
    _show_case_attr(g_tst_tc_control->tst_tc_path);
    return 0;
}

int tst_main(int argc, char **argv) {
    int ret = 0;

    if (_tc_init(argc, argv) != 0) {
        return 1;
    }

    FILE* f = fopen(g_tst_tc_control->tst_ts_result_file, "a");
    if (f == NULL) {
        msg("Error opening file %s\n", g_tst_tc_control->tst_ts_result_file);
        return 1;
    }
    long long start_time = get_timestamp_ms();
    fprintf(f, "TESTCASE\n");
    fprintf(f, "case-name: %s\n", g_tst_tc_control->tc_name);
    fprintf(f, "case-id: %s\n", get_tc_attr("用例ID"));
    fprintf(f, "case-type: case-type\n");
    fprintf(f, "case-level: %s\n", get_tc_attr("用例级别"));
    fprintf(f, "case-label: %s\n", get_tc_attr("用例标签"));
    fprintf(f, "case-steps: %s\n", get_tc_attr("测试步骤"));
    fprintf(f, "case-result-id: %s-result\n", g_tst_tc_control->tc_name);
    fprintf(f, "case-start-time: %lld\n", start_time);

    fclose(f);

    if (chdir(g_tst_tc_control->tst_tc_cwd) != 0) {
        msg("chdir to %s fail, errno: %d, %s", g_tst_tc_control->tst_tc_cwd, errno, strerror(errno));
        return 1;
    }

    if (_is_ts_setup_called()) {
        msg("tsuite setup executed, stat is %d", _get_ts_setup_stat());
    } else {
        msg("tsuite setup may not executed");
    }

    _set_tcstat(TST_INIT);

    if (_tc_setup_common(argc, argv) == 0) {
        if (_tc_setup(argc, argv) == 0) {
            msg("call _tc_setup success");
            if (_do_test(argc, argv) == 0) {
                msg("call _do_test success");
            } else {
                err("call _do_test fail");
                ret = 1;
            }
        } else {
            err("call _tc_setup success");
            ret = 1;
        }
    } else {
        err("call _tc_setup_common success");
        ret = 1;
    }
    _tc_run_complete(argc, argv);

    return ret;
}
